/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
module dwt.widgets.button;


private import dwt.dwt;

private import dwt.widgets.composite;
public import dwt.widgets.control;
private import dwt.widgets.typedlistener;
private import dwt.internal.win32.os;
private import dwt.graphics.point;
private import dwt.graphics.rectangle;
private import dwt.graphics.image;
private import dwt.events.listeners;
private import dwt.events.events;
private import dwt.util.javatypes;
private import dwt.util.util;
private import dwt.util.eventhandler;


/**
 * Instances of this class represent a selectable user interface object that
 * issues notification when pressed and released. 
 * <dl>
 * <dt><b>Styles:</b></dt>
 * <dd>ARROW, CHECK, PUSH, RADIO, TOGGLE, FLAT</dd>
 * <dd>UP, DOWN, LEFT, RIGHT, CENTER</dd>
 * <dt><b>Events:</b></dt>
 * <dd>Selection</dd>
 * </dl>
 * <p>
 * Note: Only one of the styles ARROW, CHECK, PUSH, RADIO, and TOGGLE 
 * may be specified.
 * </p><p>
 * Note: Only one of the styles LEFT, RIGHT, and CENTER may be specified.
 * </p><p>
 * Note: Only one of the styles UP, DOWN, LEFT, and RIGHT may be specified
 * when the ARROW style is specified.
 * </p><p>
 * IMPORTANT: This class is intended to be subclassed <em>only</em>
 * within the DWT implementation.
 * </p>
 */

public class Button : Control {
	Image image;
	static WNDPROC_I ButtonProc;
	static const char[] ButtonClass = "BUTTON";
	static int CheckWidth, CheckHeight;
	private static boolean staticCtor_done = false;
	static void staticCtor() {
		if(staticCtor_done) return;
		staticCtor_done = true;
		
		HBITMAP hBitmap = OS.LoadBitmap (null, OS.OBM_CHECKBOXES);
		if (hBitmap is null) {
			version(WinCE){
				CheckWidth = OS.GetSystemMetrics (OS.SM_CXSMICON);
				CheckHeight = OS.GetSystemMetrics (OS.SM_CYSMICON);	
			}else{
				CheckWidth = OS.GetSystemMetrics (OS.SM_CXVSCROLL);
				CheckHeight = OS.GetSystemMetrics (OS.SM_CYVSCROLL);
			}
		} else {
			BITMAP* bitmap = new BITMAP ();
			OS.GetObject (hBitmap, BITMAP.sizeof, bitmap);
			OS.DeleteObject (hBitmap);
			CheckWidth = bitmap.bmWidth / 4;
			CheckHeight =  bitmap.bmHeight / 3;
		}
		WNDCLASS_T lpWndClass;
		OS.GetClassInfo (null, Converter.StrToTCHARz(ButtonClass), &lpWndClass);
		ButtonProc = lpWndClass.lpfnWndProc;
	}

/**
 * Constructs a new instance of this class given its parent
 * and a style value describing its behavior and appearance.
 * <p>
 * The style value is either one of the style constants defined in
 * class <code>DWT</code> which is applicable to instances of this
 * class, or must be built by <em>bitwise OR</em>'ing together 
 * (that is, using the <code>int</code> "|" operator) two or more
 * of those <code>DWT</code> style constants. The class description
 * lists the style constants that are applicable to the class.
 * Style bits are also inherited from superclasses.
 * </p>
 *
 * @param parent a composite control which will be the parent of the new instance (cannot be null)
 * @param style the style of control to construct
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
 *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
 * </ul>
 *
 * @see DWT#ARROW
 * @see DWT#CHECK
 * @see DWT#PUSH
 * @see DWT#RADIO
 * @see DWT#TOGGLE
 * @see DWT#FLAT
 * @see DWT#LEFT
 * @see DWT#RIGHT
 * @see DWT#CENTER
 * @see Widget#checkSubclass
 * @see Widget#getStyle
 */
public this (Composite parent, int style) {
	staticCtor();
	super (parent, checkStyle (style));
}

/**
 * Adds the listener to the collection of listeners who will
 * be notified when the control is selected, by sending
 * it one of the messages defined in the <code>SelectionListener</code>
 * interface.
 * <p>
 * <code>widgetSelected</code> is called when the control is selected.
 * <code>widgetDefaultSelected</code> is not called.
 * </p>
 *
 * @param listener the listener which should be notified
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 *
 * @see SelectionListener
 * @see #removeSelectionListener
 * @see SelectionEvent
 */
public void addSelectionListener (SelectionListener listener) {
	checkWidget ();
	if (listener is null) error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (DWT.Selection,typedListener);
	addListener (DWT.DefaultSelection,typedListener);
}

LRESULT callWindowProc (int msg, int wParam, int lParam) {
	if (handle is null) return 0;
	return OS.CallWindowProc (ButtonProc, handle, msg, wParam, lParam);
}

static int checkStyle (int style) {
	style = checkBits (style, DWT.PUSH, DWT.ARROW, DWT.CHECK, DWT.RADIO, DWT.TOGGLE, 0);
	if ((style & (DWT.PUSH | DWT.TOGGLE)) != 0) {
		return checkBits (style, DWT.CENTER, DWT.LEFT, DWT.RIGHT, 0, 0, 0);
	}
	if ((style & (DWT.CHECK | DWT.RADIO)) != 0) {
		return checkBits (style, DWT.LEFT, DWT.RIGHT, DWT.CENTER, 0, 0, 0);
	}
	if ((style & DWT.ARROW) != 0) {
		style |= DWT.NO_FOCUS;
		return checkBits (style, DWT.UP, DWT.DOWN, DWT.LEFT, DWT.RIGHT, 0, 0);
	}
	return style;
}

void click () {
	/*
	* Note: BM_CLICK sends WM_LBUTTONDOWN and WM_LBUTTONUP.
	*/
	OS.SendMessage (handle, OS.BM_CLICK, 0, 0);
}

public Point computeSize (int wHint, int hHint, boolean changed) {
	checkWidget ();
	int border = getBorderWidth ();
	int width = 0;
	int height = 0;
	if ((style & DWT.ARROW) != 0) {
		if ((style & (DWT.UP | DWT.DOWN)) != 0) {
			width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
			height += OS.GetSystemMetrics (OS.SM_CYVSCROLL);
		} else {
			width += OS.GetSystemMetrics (OS.SM_CXHSCROLL);
			height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
		}
		if (wHint != DWT.DEFAULT) width = wHint;
		if (hHint != DWT.DEFAULT) height = hHint;
		width += border * 2; height += border * 2;
		return new Point (width, height);
	}
	int extra = 0;
	int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
	if ((bits & (OS.BS_BITMAP | OS.BS_ICON)) == 0) {
		HFONT oldFont = null;
		HDC hDC = OS.GetDC (handle);
		HFONT newFont = cast(HFONT)OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
		if (newFont !is null) oldFont = OS.SelectObject (hDC, newFont);
		TEXTMETRIC lptm;
		OS.GetTextMetrics (hDC, &lptm);
		int _length = OS.GetWindowTextLength(handle);
		if (_length == 0) {
			height += lptm.tmHeight;
		} else {
			extra = Math.max (8, lptm.tmAveCharWidth);
			TCHAR[] buffer = new TCHAR[_length + 1];
			OS.GetWindowText (handle, buffer.ptr, buffer.length);
			RECT rect;
			int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE;
			OS.DrawText (hDC, buffer.ptr, _length, &rect, flags);
			width += rect.right - rect.left;
			height += rect.bottom - rect.top;
		}
		if (newFont !is null) OS.SelectObject (hDC, oldFont);
		OS.ReleaseDC (handle, hDC);
	} else {
		if (image !is null) {
			Rectangle rect = image.getBounds ();
			width = rect.width;
			height = rect.height;
			extra = 8;
		}
	}
	if ((style & (DWT.CHECK | DWT.RADIO)) != 0) {
		width += CheckWidth + extra;
		height = Math.max (height, CheckHeight + 3);
	}
	if ((style & (DWT.PUSH | DWT.TOGGLE)) != 0) {
		width += 12;  height += 10;
	}
	if (wHint != DWT.DEFAULT) width = wHint;
	if (hHint != DWT.DEFAULT) height = hHint;
	width += border * 2; height += border * 2;
	return new Point (width, height);
}

int defaultBackground () {
	if ((style & (DWT.PUSH | DWT.TOGGLE)) != 0) {
		return OS.GetSysColor (OS.COLOR_BTNFACE);
	}
	return super.defaultBackground ();
}

int defaultForeground () {
	return OS.GetSysColor (OS.COLOR_BTNTEXT);
}

/**
 * Returns a value which describes the position of the
 * text or image in the receiver. The value will be one of
 * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>
 * unless the receiver is an <code>ARROW</code> button, in 
 * which case, the alignment will indicate the direction of
 * the arrow (one of <code>LEFT</code>, <code>RIGHT</code>, 
 * <code>UP</code> or <code>DOWN</code>).
 *
 * @return the alignment 
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public int getAlignment () {
	checkWidget ();
	if ((style & DWT.ARROW) != 0) {
		if ((style & DWT.UP) != 0) return DWT.UP;
		if ((style & DWT.DOWN) != 0) return DWT.DOWN;
		if ((style & DWT.LEFT) != 0) return DWT.LEFT;
		if ((style & DWT.RIGHT) != 0) return DWT.RIGHT;
		return DWT.UP;
	}
	if ((style & DWT.LEFT) != 0) return DWT.LEFT;
	if ((style & DWT.CENTER) != 0) return DWT.CENTER;
	if ((style & DWT.RIGHT) != 0) return DWT.RIGHT;
	return DWT.LEFT;
}

boolean getDefault () {
	if ((style & DWT.PUSH) == 0) return false;
	int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
	return (bits & OS.BS_DEFPUSHBUTTON) != 0;
}

/**
 * Returns the receiver's image if it has one, or null
 * if it does not.
 *
 * @return the receiver's image
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public Image getImage () {
	checkWidget ();
	return image;
}

char[] getNameText () {
	return getText ();
}

/**
 * Returns <code>true</code> if the receiver is selected,
 * and false otherwise.
 * <p>
 * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
 * it is selected when it is checked. When it is of type <code>TOGGLE</code>,
 * it is selected when it is pushed in. If the receiver is of any other type,
 * this method returns false.
 *
 * @return the selection state
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public boolean getSelection () {
	checkWidget ();
	if ((style & (DWT.CHECK | DWT.RADIO | DWT.TOGGLE)) == 0) return false;
	int state = OS.SendMessage (handle, OS.BM_GETCHECK, 0, 0);
	return (state & OS.BST_CHECKED) != 0;
}

/**
 * Returns the receiver's text, which will be an empty
 * string if it has never been set or if the receiver is
 * an <code>ARROW</code> button.
 *
 * @return the receiver's text
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public char[] getText () {
	checkWidget ();
	if ((style & DWT.ARROW) != 0) return "";	
	return OS.GetWindowText(handle);
}

boolean isTabItem () {
	//TEMPORARY CODE
	//if ((style & DWT.PUSH) != 0) return true;
	return super.isTabItem ();
}

boolean mnemonicHit (wchar ch) {
	if (!setFocus ()) return false;
	/*
	* Feature in Windows.  When a radio button gets focus, 
	* it selects the button in WM_SETFOCUS.  Therefore, it
	* is not necessary to click the button or send events
	* because this has already happened in WM_SETFOCUS.
	*/
	if ((style & DWT.RADIO) == 0) click ();
	return true;
}

boolean mnemonicMatch (wchar key) {
	wchar mnemonic = findMnemonic (getText ());
	if (mnemonic == '\0') return false;
	return Character.toUpperCase (key) == Character.toUpperCase (mnemonic);
}

void releaseWidget () {
	super.releaseWidget ();
	image = null;
}

/**
 * Removes the listener from the collection of listeners who will
 * be notified when the control is selected.
 *
 * @param listener the listener which should be notified
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 *
 * @see SelectionListener
 * @see #addSelectionListener
 */
public void removeSelectionListener (SelectionListener listener) {
	checkWidget ();
	if (listener is null) error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	if (eventTable is null) return;
	eventTable.unhook (DWT.Selection, listener);
	eventTable.unhook (DWT.DefaultSelection,listener);	
}

void selectRadio () {
	/*
	* This code is intentionally commented.  When two groups
	* of radio buttons with the same parent are separated by
	* another control, the correct behavior should be that
	* the two groups act independently.  This is consistent
	* with radio tool and menu items.  The commented code
	* implements this behavior.
	*/
//	int index = 0;
//	Control [] children = parent._getChildren ();
//	while (index < children.length && children [index] != this) index++;
//	int i = index - 1;
//	while (i >= 0 && children [i].setRadioSelection (false)) --i;
//	int j = index + 1;
//	while (j < children.length && children [j].setRadioSelection (false)) j++;
//	setSelection (true);
	Control [] children = parent._getChildren ();
	for (int i=0; i<children.length; i++) {
		Control child = children [i];
		if (this !is child) child.setRadioSelection (false);
	}
	setSelection (true);
}

/**
 * Controls how text, images and arrows will be displayed
 * in the receiver. The argument should be one of
 * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>
 * unless the receiver is an <code>ARROW</code> button, in 
 * which case, the argument indicates the direction of
 * the arrow (one of <code>LEFT</code>, <code>RIGHT</code>, 
 * <code>UP</code> or <code>DOWN</code>).
 *
 * @param alignment the new alignment 
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setAlignment (int alignment) {
	checkWidget ();
	if ((style & DWT.ARROW) != 0) {
		if ((style & (DWT.UP | DWT.DOWN | DWT.LEFT | DWT.RIGHT)) == 0) return; 
		style &= ~(DWT.UP | DWT.DOWN | DWT.LEFT | DWT.RIGHT);
		style |= alignment & (DWT.UP | DWT.DOWN | DWT.LEFT | DWT.RIGHT);
		OS.InvalidateRect (handle, null, true);
		return;
	}
	if ((alignment & (DWT.LEFT | DWT.RIGHT | DWT.CENTER)) == 0) return;
	style &= ~(DWT.LEFT | DWT.RIGHT | DWT.CENTER);
	style |= alignment & (DWT.LEFT | DWT.RIGHT | DWT.CENTER);
	int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
	bits &= ~(OS.BS_LEFT | OS.BS_CENTER | OS.BS_RIGHT);
	if ((style & DWT.LEFT) != 0) bits |= OS.BS_LEFT;
	if ((style & DWT.CENTER) != 0) bits |= OS.BS_CENTER;
	if ((style & DWT.RIGHT) != 0) bits |= OS.BS_RIGHT;
	OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
	OS.InvalidateRect (handle, null, true);
}

void setDefault (boolean value) {
	if ((style & DWT.PUSH) == 0) return;
	HWND hwndShell = menuShell ().handle;
	int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
	if (value) {
		bits |= OS.BS_DEFPUSHBUTTON;
		OS.SendMessage (hwndShell, OS.DM_SETDEFID, handle, 0);
	} else {
		bits &= ~OS.BS_DEFPUSHBUTTON;
		OS.SendMessage (hwndShell, OS.DM_SETDEFID, 0, 0);
	}
	OS.SendMessage (handle, OS.BM_SETSTYLE, bits, 1);
}

/**
 * Sets the receiver's image to the argument, which may be
 * null indicating that no image should be displayed.
 *
 * @param image the image to display on the receiver (may be null)
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
 * </ul> 
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setImage (Image image) {
	checkWidget ();
	HBITMAP hImage = null;
	int imageBits = 0, fImageType = 0;
	if (image !is null) {
		if (image.isDisposed()) error(__FILE__, __LINE__, DWT.ERROR_INVALID_ARGUMENT);
		hImage = image.handle;
		switch (image.type) {
			case DWT.BITMAP:
				imageBits = OS.BS_BITMAP;
				fImageType = OS.IMAGE_BITMAP;
				break;
			case DWT.ICON:
				imageBits = OS.BS_ICON;
				fImageType = OS.IMAGE_ICON;
				break;
			default:
				return;
		}
	}
	this.image = image;
	int newBits = OS.GetWindowLong (handle, OS.GWL_STYLE);
	int oldBits = newBits;
	newBits &= ~(OS.BS_BITMAP | OS.BS_ICON);
	newBits |= imageBits;
	if (newBits != oldBits) {
		OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
	}
	OS.SendMessage (handle, OS.BM_SETIMAGE, fImageType, hImage);
}

boolean setRadioFocus () {
	if ((style & DWT.RADIO) == 0 || !getSelection ()) return false;
	return setFocus ();
}

boolean setRadioSelection (boolean value) {
	if ((style & DWT.RADIO) == 0) return false;
	if (getSelection () != value) {
		setSelection (value);
		postEvent (DWT.Selection);
	}
	return true;
}

boolean setSavedFocus () {
	/*
	* Feature in Windows.  When a radio button gets focus, 
	* it selects the button in WM_SETFOCUS.  If the previous
	* saved focus widget was a radio button, allowing the shell
	* to automatically restore the focus to the previous radio
	* button will unexpectedly check that button.  The fix is
	* to disallow focus to be restored to radio button that is
	* not selected.
	*/
	if ((style & DWT.RADIO) != 0 && !getSelection ()) return false;
	return super.setSavedFocus ();
}

/**
 * Sets the selection state of the receiver, if it is of type <code>CHECK</code>, 
 * <code>RADIO</code>, or <code>TOGGLE</code>.
 *
 * <p>
 * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
 * it is selected when it is checked. When it is of type <code>TOGGLE</code>,
 * it is selected when it is pushed in.
 *
 * @param selected the new selection state
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setSelection (boolean selected) {
	checkWidget ();
	if ((style & (DWT.CHECK | DWT.RADIO | DWT.TOGGLE)) == 0) return;
	int flags = selected ? OS.BST_CHECKED : OS.BST_UNCHECKED;
	
	/*
	* Feature in Windows. When BM_SETCHECK is used
	* to set the checked state of a radio or check
	* button, it sets the WM_TABSTOP style.  This
	* is undocumented and unwanted.  The fix is
	* to save and restore the window style bits.
	*/
	int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
	OS.SendMessage (handle, OS.BM_SETCHECK, flags, 0);
	OS.SetWindowLong (handle, OS.GWL_STYLE, bits);     
}

/**
 * Sets the receiver's text.
 * <p>
 * This method sets the button label.  The label may include
 * the mnemonic character but must not contain line delimiters.
 * </p>
 * <p>
 * Mnemonics are indicated by an '&amp' that causes the next
 * character to be the mnemonic.  When the user presses a
 * key sequence that matches the mnemonic, a selection
 * event occurs. On most platforms, the mnemonic appears
 * underlined but may be emphasised in a platform specific
 * manner.  The mnemonic indicator character '&amp' can be
 * escaped by doubling it in the string, causing a single
 *'&amp' to be displayed.
 * </p>
 * 
 * @param string the new text
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setText (char[] string) {
	checkWidget ();
	if (string is null) string = "";
	//error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	if ((style & DWT.ARROW) != 0) return;
	int newBits = OS.GetWindowLong (handle, OS.GWL_STYLE);
	int oldBits = newBits;
	newBits &= ~(OS.BS_BITMAP | OS.BS_ICON);
	if (newBits != oldBits) {
		OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
	}
	OS.SetWindowText (handle, Converter.StrToTCHARz(string));
}


int widgetStyle () {
	int bits = super.widgetStyle ();
	if ((style & DWT.FLAT) != 0) bits |= OS.BS_FLAT;
	if ((style & DWT.ARROW) != 0) return bits | OS.BS_OWNERDRAW;
	if ((style & DWT.LEFT) != 0) bits |= OS.BS_LEFT;
	if ((style & DWT.CENTER) != 0) bits |= OS.BS_CENTER;
	if ((style & DWT.RIGHT) != 0) bits |= OS.BS_RIGHT;
	if ((style & DWT.PUSH) != 0) return bits | OS.BS_PUSHBUTTON | OS.WS_TABSTOP;
	if ((style & DWT.CHECK) != 0) return bits | OS.BS_CHECKBOX | OS.WS_TABSTOP;
	if ((style & DWT.RADIO) != 0) return bits | OS.BS_RADIOBUTTON;
	if ((style & DWT.TOGGLE) != 0) return bits | OS.BS_PUSHLIKE | OS.BS_CHECKBOX;
	return bits | OS.BS_PUSHBUTTON | OS.WS_TABSTOP;
}

char[] windowClass() {
	return ButtonClass;
}

WNDPROC_I windowProc2 () {
	return ButtonProc;
}

LDWTRESULT WM_GETDLGCODE_ (int wParam, int lParam) {
	LDWTRESULT result = super.WM_GETDLGCODE_ (wParam, lParam);
	if (result !is null) return result;
	if ((style & DWT.ARROW) != 0) {
		return new LDWTRESULT (OS.DLGC_STATIC);
	}
	return result;
}

LDWTRESULT WM_KILLFOCUS_ (int wParam, int lParam) {
	LDWTRESULT result = super.WM_KILLFOCUS_ (wParam, lParam);
	if ((style & DWT.PUSH) != 0 && getDefault ()) {
		menuShell ().setDefaultButton (null, false);
	}
	return result;
}

LDWTRESULT WM_SETFOCUS_ (int wParam, int lParam) {
	/*
	* Feature in Windows. When Windows sets focus to
	* a radio button, it sets the WM_TABSTOP style.
	* This is undocumented and unwanted.  The fix is
	* to save and restore the window style bits.
	*/
	int bits = 0;
	if ((style & DWT.RADIO) != 0) {
		bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
	}
	LDWTRESULT result = super.WM_SETFOCUS_ (wParam, lParam);
	if ((style & DWT.RADIO) != 0) {
		OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
	}
	if ((style & DWT.PUSH) != 0) {
		menuShell ().setDefaultButton (this, false);
	}
	return result;
}

LDWTRESULT wmCommandChild (int wParam, int lParam) {
	int code = wParam >> 16;
	switch (code) {
		case OS.BN_CLICKED:
		case OS.BN_DOUBLECLICKED:
			if ((style & (DWT.CHECK | DWT.TOGGLE)) != 0) {
				setSelection (!getSelection ());
			} else {
				if ((style & DWT.RADIO) != 0) {
					if ((parent.getStyle () & DWT.NO_RADIO_GROUP) != 0) {
						setSelection (!getSelection ());
					} else {
						selectRadio ();
					}
				}
			}
			postEvent (DWT.Selection);
			break;
		default : break;
	}
	return super.wmCommandChild (wParam, lParam);
}

LDWTRESULT wmDrawChild (int wParam, int lParam) {
	if ((style & DWT.ARROW) == 0) return super.wmDrawChild (wParam, lParam);
	DRAWITEMSTRUCT* strct = cast(DRAWITEMSTRUCT*)lParam;
	int uState = OS.DFCS_SCROLLLEFT;
	switch (style & (DWT.UP | DWT.DOWN | DWT.LEFT | DWT.RIGHT)) {
		case DWT.UP: uState = OS.DFCS_SCROLLUP; break;
		case DWT.DOWN: uState = OS.DFCS_SCROLLDOWN; break;
		case DWT.LEFT: uState = OS.DFCS_SCROLLLEFT; break;
		case DWT.RIGHT: uState = OS.DFCS_SCROLLRIGHT; break;
		default : break;
	}
	if (!getEnabled ()) uState |= OS.DFCS_INACTIVE;
	if ((style & DWT.FLAT) == DWT.FLAT) uState |= OS.DFCS_FLAT;
	if ((strct.itemState & OS.ODS_SELECTED) != 0) uState |= OS.DFCS_PUSHED;
	OS.DrawFrameControl (strct.hDC, &strct.rcItem, OS.DFC_SCROLL, uState);
	return null;
}

// Event Handler
public void handleSelection(Object customData, void delegate(SelectionEvent) func){
	handleEvent(new SelectionHandler(customData, DWT.Selection, func));
}
public void handleDefaultSelection(Object customData, void delegate(SelectionEvent) func) {
	handleEvent(new SelectionHandler(customData, DWT.DefaultSelection, func));
}

}
